// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLExprImpl;
// import com.alibaba.druid.sql.ast.SQLObject;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.math.BigDecimal;
// import java.util.Collections;
// import java.util.List;

#include "SQLSizeExpr.h"

#include "SQLIntegerExpr.h"
#include "SQLNumberExpr.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../../../utils/StringUtils.h"

SQLSizeExpr_Unit SQLSizeExpr_Unit::B = SQLSizeExpr_Unit(1 << 0, make_string_ptr("B"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::K = SQLSizeExpr_Unit(1 << 1, make_string_ptr("K"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::M = SQLSizeExpr_Unit(1 << 2, make_string_ptr("M"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::G = SQLSizeExpr_Unit(1 << 3, make_string_ptr("G"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::T = SQLSizeExpr_Unit(1 << 4, make_string_ptr("T"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::P = SQLSizeExpr_Unit(1 << 5, make_string_ptr("P"));
SQLSizeExpr_Unit SQLSizeExpr_Unit::E = SQLSizeExpr_Unit(1 << 6, make_string_ptr("E"));

SQLSizeExpr_Unit::SQLSizeExpr_Unit(uint64_t mask, string_ptr name)
{
  this->mask = mask;
  this->name = name;
}

std::shared_ptr<SQLSizeExpr_Unit> SQLSizeExpr_Unit::valueOf(string_ptr name)
{
  if (name == B.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&B);
  }
  else if (name == K.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&K);
  }
  else if (name == M.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&M);
  }
  else if (name == G.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&G);
  }
  else if (name == T.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&T);
  }
  else if (name == P.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&P);
  }
  else if (name == E.name)
  {
    return std::shared_ptr<SQLSizeExpr_Unit>(&E);
  }
  else
  {
    return nullptr;
  }
}

SQLSizeExpr::SQLSizeExpr()
{
}

SQLSizeExpr::SQLSizeExpr(string_ptr value, char unit)
{
  string_ptr tmp = make_string_ptr(StringUtils::charToString(unit));
  this->unit = SQLSizeExpr_Unit::valueOf(StringUtils::toUpper(tmp));
  if (value->find('.') == -1)
  {
    this->value = SQLExpr_ptr(new SQLIntegerExpr(atoi(value->c_str())));
  }
  else
  {
    this->value = SQLExpr_ptr(new SQLNumberExpr(atof(value->c_str())));
  }
}

SQLSizeExpr::SQLSizeExpr(SQLExpr_ptr value, std::shared_ptr<SQLSizeExpr_Unit> unit)
    : SQLExprImpl()
{
  this->value = value;
  this->unit = unit;
}

//@Override
void SQLSizeExpr::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLSizeExpr)))
  {
    if (this->value != nullptr)
    {
      this->value->accept(visitor);
    }
  }
  visitor->endVisit(SharedObject(SQLSizeExpr));
}

SQLObject_list_ptr SQLSizeExpr::getChildren()
{
  // return Collections.<SQLObject>singletonList(value);
  SQLObject_list_ptr tmp = SQLObject_list_ptr();
  tmp->push_back(value);
  return tmp;
}

SQLExpr_ptr SQLSizeExpr::getValue()
{
  return value;
}

void SQLSizeExpr::setValue(SQLExpr_ptr value)
{
  this->value = value;
}

std::shared_ptr<SQLSizeExpr_Unit> SQLSizeExpr::getUnit()
{
  return unit;
}

void SQLSizeExpr::setUnit(std::shared_ptr<SQLSizeExpr_Unit> unit)
{
  this->unit = unit;
}

SQLObject_ptr SQLSizeExpr::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLSizeExpr>(new SQLSizeExpr());
  std::shared_ptr<SQLSizeExpr> x = std::dynamic_pointer_cast<SQLSizeExpr>(clone_tmp);
  // SQLSizeExpr x = new SQLSizeExpr();

  if (value != nullptr)
  {
    x->setValue(std::dynamic_pointer_cast<SQLExpr>(value->clone()));
  }
  x->unit = unit;

  return x;
}

//@Override
bool SQLSizeExpr::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == nullptr || getClass() != o->getClass())
  {
    return false;
  }

  std::shared_ptr<SQLSizeExpr> that = std::dynamic_pointer_cast<SQLSizeExpr>(o);

  if (value != nullptr ? !value->equals(that->value) : that->value != nullptr)
  {
    return false;
  }
  return unit == that->unit;
}

//@Override
int SQLSizeExpr::hashCode()
{
  int result = value != nullptr ? value->hashCode() : 0;
  // result = 31 * result + (unit != nullptr ? unit->hashCode() : 0);
  result = 31 * result + (unit != nullptr ? unit->mask : 0);
  return result;
}
